package indv.Cning.cfengsecuritydemo.filter;

import com.fasterxml.jackson.databind.ObjectMapper;
import indv.Cning.cfengsecuritydemo.domain.JwtUser;
import indv.Cning.cfengsecuritydemo.jwt.JwtTokenUtil;
import io.jsonwebtoken.ExpiredJwtException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.stream.Collectors;

/**
 * @author Cfeng
 * @date 2022/7/24
 * JWT验证依赖于实现了OnceRerRequestFilter的过滤器
 * JWT过滤器主要完成从请求中获取JwtToken，token有效将token转换为UserXXXX 并且放入security上下文中
 */

@Component //创建对象放入容器
@Slf4j
@RequiredArgsConstructor
public class JwtRequestFilter extends OncePerRequestFilter {

    private final ObjectMapper objectMapper;

    private  final JwtTokenUtil jwtTokenUtil;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        //取出请求头中的认证信息authorization,jwt
        String requestTokenHeader = request.getHeader("authorization");
        String username = null;
        String jwtToken = null;
        //JWT为Bearer token格式，去掉Bearer，就是Token
        String bearerPrefix = "Bearer ";
        if(requestTokenHeader != null && requestTokenHeader.startsWith(bearerPrefix)) {
            jwtToken = requestTokenHeader.substring(7); //截取后面的部分
        } else {
            log.warn("JWT Token does not begin with Bearer String");
            if(request.getCookies() != null) {
                for(Cookie cookie : request.getCookies()) {
                    if("jwt".equals(cookie.getName())) {
                        jwtToken = cookie.getValue();
                    }
                }
            }
        }
        //上面的token要么直接从Header的authorization中获取，要么从cookies中获取
        try {
            username = jwtTokenUtil.getUserNameFromToken(jwtToken);
        } catch (IllegalArgumentException e) {
            System.out.println("Unable to get Jwt Token");
        } catch (ExpiredJwtException e) {
            System.out.println("JWT Token has expired");
        }

        //验证token,主要是用户名是否存在，当前登录账户是否为空，以及登录
        boolean isAuthentication = username != null && SecurityContextHolder.getContext().getAuthentication() == null && jwtTokenUtil.validateToken(jwtToken,username);
        if(isAuthentication) {
            //如果token有效，手动授权，并且将其设置到Spring security上下文,找到claims中的身份信息JwtUser
            String userJson = jwtTokenUtil.getClaimFromToken(jwtToken,claims -> claims.get("principal",String.class));
            //将json转为对象readValue
            JwtUser jwtUser = objectMapper.readValue(userJson,JwtUser.class);
            //完成授权
            User userDetails = new User(jwtUser.getUserName(),jwtUser.getUserPwd(),jwtUser.getAuthorities().stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList()));
            //token转换
            UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(userDetails,null,userDetails.getAuthorities());
            usernamePasswordAuthenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
            //放入上下文
            SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
        }
        filterChain.doFilter(request,response);
    }
}
